package cg.cmm.redis.aspect;

import cg.cmm.base.utils.EvaluationUtils;
import cg.cmm.redis.annotation.RedisLock;
import jodd.util.StringUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.expression.EvaluationContext;

import java.lang.reflect.Method;
import java.util.Optional;

/**
 * Redis 分布式锁切面类
 *
 * @author yingxli
 */
@Aspect
@Slf4j
@AllArgsConstructor
public class RedisLockAspect {

    /**
     * Redis 客户端
     */
    private final RedissonClient redissonClient;

    /**
     * 环绕通知，实现加锁解锁逻辑
     *
     * @param proceedingJoinPoint 代理方法
     * @return 目标方法返回值
     * @throws Throwable 与目标方法异常一致
     */
    @Around(
            "@annotation(cg.cmm.redis.annotation.RedisLock) || " +
                    "@within(cg.cmm.redis.annotation.RedisLock)"
    )
    public Object lock(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = ms.getMethod();

        // 使用 AnnotatedElementUtils 获取注解，支持方法和类级别的注解
        RedisLock redisLock = AnnotatedElementUtils.findMergedAnnotation(method, RedisLock.class);

        String redisLockKey = Optional.ofNullable(redisLock)
                .map(RedisLock::value)
                .orElse(null);
        String expression = Optional.ofNullable(redisLock)
                .map(RedisLock::expression)
                .orElse(null);

        if (StringUtil.isNotBlank(expression)) {
            // 解析 SPEL
            EvaluationContext context = EvaluationUtils.getContext(proceedingJoinPoint.getArgs(), method);
            try {
                redisLockKey = EvaluationUtils.getValue(context, expression, String.class);
            } catch (Exception e) {
                // SPEL 表达式异常，获取 value 的值
                log.error("解析 SPEL {} 异常", expression, e);
            }
        }

        // key 为空，不加锁，log 提示
        if (StringUtil.isBlank(redisLockKey)) {
            log.info("redis lock key empty, skipped");
            return proceedingJoinPoint.proceed();
        }

        // 加锁 -> 执行 -> 解锁
        RLock lock = redissonClient.getLock(redisLockKey);
        try {
            log.info("try to get a lock, key: {}...", redisLockKey);
            lock.lock();
            return proceedingJoinPoint.proceed();
        } catch (Exception e) {
            log.error("execute error: ", e);
            throw e;
        } finally {
            log.info("try to release a lock, key: {}...", redisLockKey);
            lock.unlock();
        }
    }
}